/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate alloc;
use crate::vec;

use crate::common::constants::*;
use crate::common::types::*;
use crate::{Box, log_e, log_i, Vec};
use crate::commands::common_command::{ScheduleInfo, CredentialInfo};
use crate::commands::enrollment_commands::{EnrollResultInfo, EnrollParam};
use crate::traits::user_db_manager::CredentialFilter;
use crate::traits::user_db_manager::Credential;
use crate::traits::context_manager::EnrollContext;
use crate::contexts::base_enroll_context::BaseEnrollContext;
use crate::utils::AuthToken;

pub struct PinEnrollContext {
    enroll_context: BaseEnrollContext,
}

impl PinEnrollContext {
    pub fn new(enroll_param: &EnrollParam) -> Self {
        Self {
            enroll_context: BaseEnrollContext::new(enroll_param),
        }
    }
}

impl EnrollContext for PinEnrollContext {
    fn schedule_id(&self) -> u64 {
        self.enroll_context.get_schedule_id()
    }

    fn begin(&mut self, _auth_token: &AuthToken, enroll_param: &EnrollParam) -> Result<Vec<ScheduleInfo>, ErrorCode> {
        self.enroll_context.check_operate_permission(enroll_param)?;
        let user_id = enroll_param.user_id;
        let auth_type = enroll_param.auth_type;

        let filter: CredentialFilter = Box::new(move |credential: &Credential| {
            credential.user_id == user_id && credential.auth_type == auth_type
        });

        self.enroll_context.generate_enroll_schedule(enroll_param, filter)?;
        let executor_message = self.enroll_context.create_executor_message(&self.enroll_context.get_enroll_schedule())?;

        let mut schedule_info_list = Vec::new();
        let schedule_info = ScheduleInfo {
            schedule_id: self.enroll_context.get_schedule_id(),
            template_ids: self.enroll_context.get_template_id_list().to_vec(),
            auth_type: auth_type,
            executor_matcher: self.enroll_context.get_executor_matcher(enroll_param)?,
            schedule_mode: ScheduleMode::Enroll,
            executor_indexes: self.enroll_context.get_executor_indexes(enroll_param)?,
            executor_messages: vec![executor_message.clone()],
        };
        schedule_info_list.push(schedule_info);
        Ok(schedule_info_list)
    }

    fn update_result(&mut self, user_id: i32, schedule_result: &Vec<u8>) -> Result<EnrollResultInfo, ErrorCode> {
        let executor_result = self.enroll_context.parse_executor_message(schedule_result)?;
        if executor_result.is_success() {
            log_e!("Executor enrollment failed with result: {}", executor_result.result());
            return Err(ErrorCode::try_from(executor_result.result()).unwrap());
        }

        let mut enroll_result_info = self.enroll_context.add_credential(&executor_result)?;
        let auth_token = self.enroll_context.generate_enroll_token(user_id, enroll_result_info.credential_id)?;
        enroll_result_info.auth_token = auth_token;
        Ok(enroll_result_info)
    }
}
